package org.test4j.mock.faking.meta;

import org.test4j.mock.startup.Startup;

import java.util.Iterator;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * <pre>
 *  KEY: mock up no
 *  VALUE: <class#name(desc): fake method>
 *  METHOD_ID: class#name(parameter desc) without return
 * </pre>
 *
 * @author darui.wu
 */
public class FakeStates extends ConcurrentHashMap<Long, FakeMethods> {
    /**
     * Startup阶段MockUp类
     * key: mock up no
     * value: (MethodDesc: FakeMethod)
     */
    private static final FakeStates initFakes = new FakeStates(32);
    /**
     * 测试过程中MockUp类
     * key: mock up no
     * value: (MethodDesc: FakeMethod)
     */
    private static final FakeStates testFakes = new FakeStates(32);

    public FakeStates(int initialCapacity) {
        super(initialCapacity);
    }

    private FakeMethod lastMethod(MethodId declared) {
        long fakeId = 0;
        FakeMethod last = null;
        for (Entry<Long, FakeMethods> entry : this.entrySet()) {
            FakeMethods fakeMethods = entry.getValue();
            FakeMethod matched = fakeMethods.findMethod(declared);
            if (matched == null) {
                continue;
            }
            Set<Integer> fakeHashCodes = matched.fake.fakedHashCodes;
            if (fakeHashCodes.isEmpty()) {
                long curr = entry.getKey();
                if (curr > fakeId) {
                    last = matched;
                    fakeId = entry.getKey();
                }
            } else if (fakeHashCodes.contains(declared.targetHashCode)) {
                return matched;
            }
        }
        return last;
    }

    /**
     * 注册mock行为
     *
     * @param fakeMethods
     */
    public static void register(FakeMethods fakeMethods) {
        if (Startup.initializing) {
            initFakes.put(fakeMethods.fakeSeqNo, fakeMethods);
        } else {
            testFakes.put(fakeMethods.fakeSeqNo, fakeMethods);
        }
    }

    public static long getMaxFakeId() {
        long max = 0L;
        for (Long curr : testFakes.keySet()) {
            if (max < curr) {
                max = curr;
            }
        }
        return max;
    }

    /**
     * 回滚Fake Classes到上一个保留点 {@link #getMaxFakeId()}
     *
     * @param previousMaxFakeId
     */
    public static synchronized Long rollback(Long previousMaxFakeId) {
        if (previousMaxFakeId == null) {
            return null;
        }
        Iterator<Entry<Long, FakeMethods>> it = testFakes.entrySet().iterator();
        while (it.hasNext()) {
            Entry<Long, FakeMethods> entry = it.next();
            if (entry.getKey() > previousMaxFakeId) {
                entry.getValue().clear();
                it.remove();
            }
        }
        return Long.MAX_VALUE;
    }

    public static FakeMethod getLastMethod(MethodId real) {
        FakeMethod fakeMethod = testFakes.lastMethod(real);
        if (fakeMethod == null) {
            return initFakes.lastMethod(real);
        } else {
            return fakeMethod;
        }
    }
}